package com.lambkit.web.template;

import cn.hutool.log.StaticLog;
import com.jfinal.kit.Prop;
import com.jfinal.kit.StrKit;
import com.lambkit.core.Lambkit;
import com.lambkit.core.service.ScanFileProcess;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class TemplateFolderPro implements ITemplateFolderPro {

    @Override
    public List<Template> getInstalledTemplates(String templatePath, String templateType, String templateId) {
        List<Template> finalTemplatelist = new ArrayList<Template>();
        if(TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_DATABASE) {
            Template template = new Template(templateId);
            finalTemplatelist.add(template);
            return finalTemplatelist;
        }
        String path = templatePath;
        if(TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_RESOURCES) {
            try {
                URL url = getURL(templatePath);
                path = url.getPath();
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        } else if(TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_FILE
        || TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_WEBAPP) {
            path = TemplateManager.me().getBaseTemplatePath() + templatePath;
        } else if(!templatePath.startsWith("classpath:")) {
            path = Lambkit.context().resourceService().getWebRootPath() + templatePath;
        }
//        if(Lambkit.context().getDevMode()) {
//            StaticLog.info("template path: " + path);
//        }
        //System.out.println(getClass().getName() + " template path: " + path);
        Lambkit.context().resourceService().scanFolder(path, Lambkit.context().getResourcePathType(), new ScanFileProcess() {
            @Override
            public boolean process(File file) {
                if(file.isDirectory()) {
                    File configFile = new File(file, "template.txt");
                    if (configFile.exists() && configFile.isFile()) {
                        Prop prop = new Prop(configFile, "utf-8");
                        String type = prop.get("type");
                        //System.out.println(getClass().getName() + " template type: " + type);
                        if(StrKit.isBlank(templateType) || templateType.equalsIgnoreCase(type)) {
                            Template template = new Template(templatePath, prop.getProperties(), file.getAbsolutePath());
                            String[] files = configFile
                                    .getParentFile()
                                    .list((dir, name) -> name.endsWith(".html"));
                            if (files != null && files.length > 0) {
                                template.setHtmls(Arrays.asList(files));
                            }
                            finalTemplatelist.add(template);
                        }
                        return true;
                    }
                }
                return false;
            }
            @Override
            public boolean process(JarFile jarFile, JarEntry jarEntry) {
                String fileName = jarEntry.getName();
                if(fileName.endsWith("template.txt")) {
                    Prop prop = new Prop();
                    try {
                        InputStream inputStream = jarFile.getInputStream(jarEntry);
                        prop.getProperties().load(new InputStreamReader(inputStream, "utf-8"));
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    String type = prop.get("type");
                    //System.out.println(getClass().getName() + " template type: " + type);
                    if(StrKit.isBlank(templateType) || templateType.equalsIgnoreCase(type)) {
                        String filePath = "classpath:" + fileName.substring(0, fileName.length()-12);
                        List<String> htmls = htmls(filePath);
                        Template template = new Template(templatePath, prop.getProperties(), filePath);
                        template.setHtmls(htmls);
                        finalTemplatelist.add(template);
                    }
                }
                return false;
            }
        });
        return finalTemplatelist;
    }

    private List<String> htmls(String path) {
        List<String> htmls = new ArrayList<String>();
        Lambkit.context().resourceService().scanFolder(path, Lambkit.context().getResourcePathType(), new ScanFileProcess() {
            @Override
            public boolean process(File file) {
                if(!file.isDirectory()) {
                    String name = file.getName();
                    if(name.endsWith(".html")) {
                        htmls.add(file.getPath());
                    }
                }
                return false;
            }
            @Override
            public boolean process(JarFile jarFile, JarEntry jarEntry) {
                String fileName = jarEntry.getName();
                if(fileName.endsWith(".html")) {
                    htmls.add(fileName);
                }
                return false;
            }
        });
        return htmls;
    }

    private void scanTemplateFloders(File file, List<File> list) {
        if (file.isDirectory()) {
            File configFile = new File(file, "template.txt");
            if (configFile.exists() && configFile.isFile()) {
                list.add(file);
            } else {
                File[] files = file.listFiles();
                if (null != files) {
                    for (File f : files) {
                        if (f.isDirectory()) {
                            scanTemplateFloders(f, list);
                        }
                    }
                }
            }
        }
    }

    @Override
    public Template getTemplate(String templatePath, String templateId) {
        if(StrKit.isBlank(templateId)) {
            return null;
        }
        if(TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_DATABASE) {
            Template template = new Template(templateId);
            return template;
        }
        String path = templatePath;
        if(TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_RESOURCES) {
            try {
                URL url = getURL(templatePath);
                path = url.getPath();
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        } else if(TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_FILE
                || TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_WEBAPP) {
            path = TemplateManager.me().getBaseTemplatePath() + templatePath;
        } else if(!templatePath.startsWith("classpath:")) {
            path = Lambkit.context().resourceService().getWebRootPath() + templatePath;
        }
        final Template[] finalTemplate = {null};
        Lambkit.context().resourceService().scanFolder(path, Lambkit.context().getResourcePathType(), new ScanFileProcess() {
            @Override
            public boolean process(File file) {
                if(file.isDirectory()) {
                    File configFile = new File(file, "template.txt");
                    if (configFile.exists() && configFile.isFile()) {
                        Prop prop = new Prop(configFile, "utf-8");
                        String id = prop.get("id");
                        if (templateId.equals(id)) {
                            finalTemplate[0] = new Template(templatePath, prop.getProperties(), file.getAbsolutePath());
                            String[] files = configFile
                                    .getParentFile()
                                    .list((dir, name) -> name.endsWith(".html"));
                            if (files != null && files.length > 0) {
                                finalTemplate[0].setHtmls(Arrays.asList(files));
                            }
                        }
                        return true;
                    }
                }
                return false;
            }
            @Override
            public boolean process(JarFile jarFile, JarEntry jarEntry) {
                String fileName = jarEntry.getName();
                if(fileName.endsWith("template.txt")) {
                    Prop prop = new Prop();
                    try {
                        InputStream inputStream = jarFile.getInputStream(jarEntry);
                        prop.getProperties().load(new InputStreamReader(inputStream, "utf-8"));
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    String id = prop.get("id");
                    if (id.equals(templateId)) {
                        String filePath = "classpath:" + fileName.substring(0, fileName.length()-12);
                        List<String> htmls = htmls(filePath);
                        finalTemplate[0] = new Template(templatePath, prop.getProperties(), filePath);
                        finalTemplate[0].setHtmls(htmls);
                    }
                    return true;
                }
                return false;
            }
        });
        return finalTemplate[0];
    }

    @Override
    public String getCurrentWebPath(TemplateFolder templateFolder, HttpServletRequest request) {
        return templateFolder.getTemplatePath() + "/" + templateFolder.getCurrentTemplate().getFolder(request);
	}

    @Override
    public String getCurrentFilePath(TemplateFolder templateFolder, HttpServletRequest request, String file) {
        if(TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_CLASSPATH) {
            return templateFolder.getTemplatePath() + File.separator + templateFolder.getCurrentTemplate().getFolder(request) + File.separator + file;
        } else if(TemplateManager.me().getTemplatePathType()==TemplateManager.PATH_TYPE_DATABASE) {
            return "/" + templateFolder.getCurrentTemplateId();
        }
        return Lambkit.context().resourceService().getWebRootPath()
                + templateFolder.getTemplatePath()
                + File.separator
                + templateFolder.getCurrentTemplate().getFolder(request)
                + File.separator
                + file;
    }

    public URL getURL(String resourceLocation) throws FileNotFoundException {
        //Assert.notNull(resourceLocation, "Resource location must not be null");
        if (resourceLocation.startsWith("classpath:")) {
            String path = resourceLocation.substring("classpath:".length());
            ClassLoader cl = getDefaultClassLoader();
            URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
            if (url == null) {
                String description = "class path resource [" + path + "]";
                throw new FileNotFoundException(description +
                        " cannot be resolved to URL because it does not exist");
            }
            return url;
        }
        try {
            // try URL
            return new URL(resourceLocation);
        }
        catch (MalformedURLException ex) {
            // no URL -> treat as file path
            try {
                return new File(resourceLocation).toURI().toURL();
            }
            catch (MalformedURLException ex2) {
                throw new FileNotFoundException("Resource location [" + resourceLocation +
                        "] is neither a URL not a well-formed file path");
            }
        }
    }

    public ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        }
        catch (Throwable ex) {
            // Cannot access thread context ClassLoader - falling back...
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = this.getClass().getClassLoader();
            if (cl == null) {
                // getClassLoader() returning null indicates the bootstrap ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                }
                catch (Throwable ex) {
                    // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
                }
            }
        }
        return cl;
    }
}